package org.netty.framework.tcpserver.util;

import java.lang.reflect.Field;
import java.util.Arrays;

import org.netty.framework.core.codec.CustomMsg;
import org.netty.framework.core.codec.Header;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.netty.buffer.ByteBuf;

/**
 * 数据包操作的工具类
 * 
 * @author chenfanglin
 * @date 2018年9月28日下午3:56:15
 *
 */
public final class PacketUtil {

	private static final Logger logger = LoggerFactory.getLogger(PacketUtil.class);

	/**
	 * 解析数据包的包头
	 * 
	 * @param frame
	 * @return
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 */
	public static Header parsePacketHeader(ByteBuf frame, Class<?> clazz) throws Exception {
		// 计算实际接受到的数据包的总长度
		int actualLen = frame.readableBytes();
		Field[] fields = getHeaderFields(clazz);
		Header header = (Header) clazz.newInstance();
		for (Field field : fields) {
			field.setAccessible(true);
			Class<?> fieldType = field.getType();
			if (Integer.TYPE == fieldType || Integer.class == fieldType) {
				int intValue = frame.readInt();
				field.setInt(header, intValue);
			}
			if (Long.TYPE == fieldType || Long.class == fieldType) {
				long longValue = frame.readLong();
				field.setLong(header, longValue);
			}
			if (Short.TYPE == fieldType || Short.class == fieldType) {
				short shortValue = frame.readShort();
				field.setShort(header, shortValue);
			}
			if (Byte.TYPE == fieldType || Byte.class == fieldType) {
				byte byteValue = frame.readByte();
				field.setByte(header, byteValue);
			}
			if (Boolean.TYPE == fieldType || Boolean.class == fieldType) {
				boolean booleanValue = frame.readBoolean();
				field.setBoolean(header, booleanValue);
			}
		}

		if (checkPackageTotalLength(actualLen, header.getTotalLength())) {
			return null;
		}

		if (logger.isDebugEnabled()) {
			logger.debug("Total Length:{},Head Length:{},Body Length:{}", actualLen, header.getHeaderLength(),
					actualLen - header.getHeaderLength());
		}
		return header;
	}

	/**
	 * 查询数据包的总长度
	 * 
	 * @param actualLen
	 *            实际接受到的数据包总长度
	 * @param totalLen
	 *            包头中标识的数据包总长度
	 * @return 当接受到的长度与实际长度不一致时，返回true
	 */
	private static boolean checkPackageTotalLength(int actualLen, int totalLen) {
		// 当实际接受到的包的长度与包头上的长度不同,则数据包异常(其中有4个字节是用来表示消息长度的)
		if (actualLen != (totalLen + 4)) {
			logger.warn("实际接受到的数据包总长度={},包头中标识的数据包总长度={}", actualLen, totalLen);
			return true;
		}
		return false;
	}

	/**
	 * 获取消息头及其父类的所有属性
	 * 
	 * @param clazz
	 * @return
	 */
	private static Field[] getHeaderFields(Class<?> clazz) {
		if (clazz.isAssignableFrom(Header.class)) {
			return clazz.getDeclaredFields();
		}
		Field[] currentFields = clazz.getDeclaredFields();
		Field[] superFields = getHeaderFields(clazz.getSuperclass());
		Field[] destFields = new Field[superFields.length + currentFields.length];
		System.arraycopy(superFields, 0, destFields, 0, superFields.length);
		System.arraycopy(currentFields, 0, destFields, superFields.length, currentFields.length);
		return destFields;
	}

	/**
	 * 根据包体的长度从ByteBuf中读取数据到字节数组中
	 * 
	 * @param frame
	 * @param header
	 * @return
	 */
	public static byte[] readPacketBody(ByteBuf frame, Header header) {
		int bodyLength = header.getTotalLength() + 4 - header.getHeaderLength();
		byte[] body = new byte[bodyLength];
		frame.readBytes(body);
		if (logger.isDebugEnabled()) {
			logger.debug("Body Content:{},writeIndex:{}", Arrays.toString(body), frame.readerIndex());
		}
		return body;
	}

	/**
	 * 构造数据包的头
	 *
	 * @param out
	 * @param header
	 * @return 返回包头的长度
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 */
	public static int buildPackageHeader(ByteBuf out, CustomMsg<?> msg) throws Exception {
		Header header = msg.getHeader();
		Field[] fields = getHeaderFields(header.getClass());
		int headLength = 0;
		for (Field field : fields) {
			field.setAccessible(true);
			Class<?> fieldType = field.getType();
			if (Integer.TYPE == fieldType || Integer.class == fieldType) {
				int intValue = field.getInt(header);
				out.writeInt(intValue);
				headLength = headLength + 4;
			}
			if (Long.TYPE == fieldType || Long.class == fieldType) {
				long longValue = field.getLong(header);
				out.writeLong(longValue);
				headLength = headLength + 8;
			}
			if (Short.TYPE == fieldType || Short.class == fieldType) {
				short shortValue = field.getShort(header);
				out.writeShort(shortValue);
				headLength = headLength + 2;
			}
			if (Byte.TYPE == fieldType || Byte.class == fieldType) {
				byte byteValue = field.getByte(header);
				out.writeByte(byteValue);
				headLength = headLength + 1;
			}
			if (Boolean.TYPE == fieldType || Boolean.class == fieldType) {
				boolean booleanValue = field.getBoolean(header);
				out.writeBoolean(booleanValue);
				headLength = headLength + 1;
			}
		}
		// 此处设置消息头的长度
		out.setInt(4, headLength);
		return headLength;
	}
}
